/* ******************************************************************************
 *  封装实现一个SqliteHelper类，提供简单的sqlite数据库操作接口，完成数据的基础增删改查操作。
 *     1. 创建/打开数据库文件
 *     2. 针对打开的数据库执行操作
 *          a. 表的操作
 *          b. 数据的操作
 *     3. 关闭数据库
 */

#include <iostream>
#include <string>
#include <sqlite3.h>

class SqliteHelper
{
    /****************************************
     *  void*: 是设置的在回调时传⼊的arg参数
     *  int: 一行中数据的列数
     *  char**: 存储⼀行数据的字符指针数组
     *  char**: 每⼀列的字段名称
     */
    typedef int (*SqliteCallback)(void *, int, char **, char **);

public:
    SqliteHelper(const std::string &dbfile)
        : dbfile_(dbfile), handler_(nullptr) {}

    /******************************************************************************************
     *  int sqlite3_open_v2(const char *filename, sqlite3 **ppDb, int flags, const char *zVfs );
     *  flag：
     *    SQLITE_OPEN_READWRITE -- 以可读可写⽅式打开数据库⽂件
     *    SQLITE_OPEN_CREATE -- 不存在数据库⽂件则创建
     *    SQLITE_OPEN_NOMUTEX -- 多线程模式，只要不同的线程使⽤不同的连接即可保证线程安全
     *    SQLITE_OPEN_FULLMUTEX -- 串⾏化模式
     *    
     *    返回SQLITE_OK表示成功 
     */
    bool open(int save_level = SQLITE_OPEN_FULLMUTEX)
    {
        int ret = sqlite3_open_v2(dbfile_.c_str(), &handler_, SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE | save_level, nullptr);
        if(ret != SQLITE_OK) 
        {
            std::cout << "create/open sqlite database failed: "
                      << sqlite3_errmsg(handler_) << "\n";
            return false;
        }
        return true;
    } 

    /*************************************************************
     *  int sqlite3_exec(sqlite3*, char *sql, 
     *                   int (*callback) (void*, int, char**, char**), 
     *                   void* arg, char **err)
     * 
     *  int (*callback) (void*, int, char**, char**)
     *     void*: 是设置的在回调时传入的arg参数
     *     int: 一行中数据的列数
     *     char**: 存储一行数据的字符指针数组
     *     char**: 每一列的字段名称
     *  这个回调函数有个int返回值，成功处理的情况下必须返回0，返回非0会触发ABORT退出程序
     *  返回SQLITE_OK表示成功 
     */
    bool exec(const std::string &sql, SqliteCallback callback, void *arg)
    {
        int ret = sqlite3_exec(handler_, sql.c_str(), callback, arg, nullptr);
        if(ret != SQLITE_OK)
        {
            std::cout << "execut sql "<< sql <<" failed: "
                      << sqlite3_errmsg(handler_) << "\n";
            return false;
        }
        return true;
    }

    /****************************************************
     * int sqlite3_close(sqlite3* db); 成功返回SQLITE_OK
     * int sqlite3_close_v2(sqlite3*); 推荐使用--无论如何都会返回SQLITE_OK
     */
    void close()
    {
        if(handler_) 
            sqlite3_close_v2(handler_);
    }

private:
    std::string dbfile_; // 操作文件名
    sqlite3 *handler_;   // 数据库句柄
};